package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"math/rand"
	rand2 "math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/ebfe/keccak"
	"github.com/gofrs/uuid"
	"github.com/imroc/req"
	"github.com/jzelinskie/whirlpool"
	"golang.org/x/crypto/blake2b"
	"golang.org/x/crypto/blake2s"
	"golang.org/x/crypto/ripemd160"
)

var UserMedals map[int]Medals = map[int]Medals{}
var UserMedalsLock *sync.RWMutex = &sync.RWMutex{}

type Medals []Medal

type Medal struct {
	NickName     string //主播名字
	RoomID       int64  //房间id
	TargetID     int64  //主播uid
	MedalID      int64  //勋章id
	Level        int64  //勋章等级
	MedalName    string //勋章名字
	Intimacy     int64  //勋章当前经验
	NextIntimacy int64  //勋章满经验
	DayLimit     int64  //勋章今日经验
	TodayFeed    int64  //勋章今日最大经验
	IsLighted    int64  //勋章亮灰
}

func FansMedal() {
	type MedalInfo struct {
		TargetID     int64  `json:"target_id"`     //主播uid
		MedalID      int64  `json:"medal_id"`      //勋章id
		Level        int64  `json:"level"`         //勋章等级
		MedalName    string `json:"medal_name"`    //勋章名字
		Intimacy     int64  `json:"intimacy"`      //勋章当前经验
		NextIntimacy int64  `json:"next_intimacy"` //勋章满经验
		DayLimit     int64  `json:"day_limit"`     //勋章今日经验
		TodayFeed    int64  `json:"today_feed"`    //勋章今日最大经验
		IsLighted    int64  `json:"is_lighted"`    //勋章亮灰
	}
	type AnchorInfo struct {
		NickName string `json:"nick_name"` //主播名字
	}
	type RoomInfo struct {
		RoomID       int64 `json:"room_id"`       //房间id
		LivingStatus int64 `json:"living_status"` //是否直播
	}
	type PageInfo struct {
		TotalPage int64 `json:"total_page"` //总页数
	}
	type List struct {
		Medal      MedalInfo  `json:"medal"`
		AnchorInfo AnchorInfo `json:"anchor_info"` //->主播名字
		RoomInfo   RoomInfo   `json:"room_info"`   //->房间id
	}
	type Data struct {
		List        []List   `json:"list"`
		SpecialList []List   `json:"special_list"`
		PageInfo    PageInfo `json:"page_info"`
	}
	type MedalRes struct {
		Code    int64  `json:"code"`
		Message string `json:"message"`
		Data    Data   `json:"data"`
	}
	var body MedalRes
	for userid := range Btoml.BiliUser {
		myUID := cookie2uid(Btoml.BiliUser[userid].Cookie)
		var myMedals Medals = Medals{}
		for page := 1; true; page++ {
			uri := SignHD("https://api.live.bilibili.com/xlive/app-ucenter/v1/fansMedal/panel?access_key=" + Btoml.BiliUser[userid].AccessToken + "&actionKey=appkey&appkey=dfca71928277209b&area_id=0&area_parent_id=0&build=6830300&c_locale=zh_CN&channel=huawei&device=android&disable_rcmd=0&light_status=0&mobi_app=android_hd&page=" + strconv.Itoa(page) + "&page_size=50&platform=android&room_id=0&s_locale=zh_CN&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%226.83.0%22%2C%22abtest%22%3A%22%22%7D&target_id=0&ts=" + StrTime())
			r, err := req.C().R().SetResult(&body).Get(uri)
			if err != nil {
				printf("fansMedal user %v err: %v", userid, err)
				return
			}
			if r.IsError() {
				print("fansMedal response isError")
				return
			}
			if body.Code != 0 {
				printf("fansMedal user %v err: %v", userid, body.Message)
				return
			}
			Lists := append(body.Data.SpecialList, body.Data.List...)
			for _, v := range Lists {
				myMedals = append(myMedals, Medal{
					NickName:     v.AnchorInfo.NickName,
					RoomID:       v.RoomInfo.RoomID,
					TargetID:     v.Medal.TargetID,
					MedalID:      v.Medal.MedalID,
					Level:        v.Medal.Level,
					MedalName:    v.Medal.MedalName,
					Intimacy:     v.Medal.Intimacy,
					NextIntimacy: v.Medal.NextIntimacy,
					DayLimit:     v.Medal.DayLimit,
					TodayFeed:    v.Medal.TodayFeed,
					IsLighted:    v.Medal.IsLighted,
				})
			}
			if page == int(body.Data.PageInfo.TotalPage) {
				UserMedalsLock.Lock()
				UserMedals[myUID] = myMedals
				UserMedalsLock.Unlock()
				break
			}
		}
	}
}

func hsign0(s string) string {
	//0号算法 SHA224
	a := sha256.New224()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign1(s string) string {
	//1号算法 SHA256
	a := sha256.New()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign2(s string) string {
	//2号算法 SHA384
	a := sha512.New384()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign3(s string) string {
	//3号算法 SHA512
	a := sha512.New()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign4(s string) string {
	//4号算法 SHA3-224
	a := keccak.NewSHA3224()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign5(s string) string {
	//5号算法 SHA3-256
	a := keccak.NewSHA3256()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign6(s string) string {
	//6号算法 SHA3-384
	a := keccak.NewSHA3384()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign7(s string) string {
	//7号算法 SHA3-512
	a := keccak.NewSHA3512()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign8(s string) string {
	//8号算法 BLAKE2b512
	a, _ := blake2b.New512([]byte{})
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign9(s string) string {
	//9号算法 BLAKE2s_256
	a, _ := blake2s.New256([]byte{})
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign10(s string) string {
	//10号算法 ripemd160
	a := ripemd160.New()
	a.Write([]byte(s))
	return hex.EncodeToString(a.Sum(nil))
}
func hsign11(s string) string {
	// 11号算法 whirlpool
	w := whirlpool.New()
	w.Write([]byte(s))
	return hex.EncodeToString(w.Sum(nil))
}

func HeartBeat(userid int) {
	user := Btoml.BiliUser[userid]
	UserMedalsLock.RLock()
	myMedals := UserMedals[cookie2uid(Btoml.BiliUser[userid].Cookie)]
	UserMedalsLock.RUnlock()
	for _, medal := range myMedals {
		go func(medal Medal) {
			for {
				randSleep(0, 30, userid)
				go Danmu(userid, int(medal.RoomID))
				var (
					buvid   = cookie2buvid(Btoml.BiliUser[userid].Cookie)
					tuuid   = randuuid()
					guid    = randStr(43, 4)
					visitid = randStr(32, 4)
				)
				type Data struct {
					Timestamp         int64   `json:"timestamp"`
					HeartbeatInterval int64   `json:"heartbeat_interval"`
					SecretKey         string  `json:"secret_key"`
					SecretRule        []int64 `json:"secret_rule"`
				}
				type MobileEntryRes struct {
					Code    int64  `json:"code"`
					Message string `json:"message"`
					Data    Data   `json:"data"`
				}
				var body MobileEntryRes
				roomID := strconv.Itoa(int(medal.RoomID))
				uri := "https://live-trace.bilibili.com/xlive/data-interface/v1/heartbeat/mobileEntry"
				data := SignHD(`access_key=` + Btoml.BiliUser[userid].AccessToken + `&actionKey=appkey&appkey=dfca71928277209b&area_id=0&build=6050500&buvid=` + buvid + `&c_locale=zh_CN&channel=huawei&client_ts=` + StrTime() + `&device=android&heart_beat=%5B%5D&is_patch=0&mobi_app=android_hd&parent_id=0&platform=android&room_id=` + roomID + `&s_locale=zh_CN&seq_id=0&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%226.5.0%22%2C%22abtest%22%3A%22%22%7D&ts=` + StrTime() + `&uuid=` + tuuid)
				r, err := req.C().R().SetContentType("application/x-www-form-urlencoded").SetBody(data).SetResult(&body).Post(uri)
				if err != nil {
					printf("mobileEntry err: %v", err)
					break
				}
				if r.IsError() {
					print("mobileEntry response isError")
					break
				}
				if body.Code != 0 {
					printf("mobileEntry err: %v", body.Message)
					break
				}
				//printf("用户%v %v %v %v %v", userid, user.UserName, medal.NickName, medal.MedalName, 0)
				printf("用户%v %v 已进入 %v 的直播间  %v|%v", userid, user.UserName, medal.NickName, medal.MedalName, medal.Level)
				for seqID := 1; true; seqID++ {
					time.Sleep(time.Second * time.Duration(body.Data.HeartbeatInterval))
					uri := "https://live-trace.bilibili.com/xlive/data-interface/v1/heartbeat/mobileHeartBeat"
					var (
						timestamp = strconv.Itoa(int(body.Data.Timestamp))
						secretKey = body.Data.SecretKey
						upID      = strconv.Itoa(int(medal.TargetID))
						clientTs  = StrTime()
						watchTime = strconv.Itoa(int(body.Data.HeartbeatInterval))
					)
					clientSign := `{"platform":"android","uuid":"` + tuuid + `","buvid":"` + buvid + `","seq_id":"` + strconv.Itoa(seqID) + `","room_id":"` + roomID + `","parent_id":"1","area_id":"207","timestamp":"` + timestamp + `","secret_key":"` + secretKey + `","watch_time":"` + watchTime + `","up_id":"` + upID + `","up_level":"35","jump_from":"27002","gu_id":"` + guid + `","play_type":"","play_url":"","s_time":"0","data_behavior_id":"","data_source_id":"","up_session":"","visit_id":"` + visitid + `","watch_status":"","click_id":"","session_id":"-99998","player_type":"0","client_ts":"` + clientTs + `"}`
					funcs := []func(string) string{hsign0, hsign1, hsign2, hsign3, hsign4, hsign5, hsign6, hsign7, hsign8, hsign9, hsign10, hsign11}
					for _, v := range body.Data.SecretRule {
						clientSign = funcs[v](clientSign)
					}
					data := SignHD(`access_key=` + Btoml.BiliUser[userid].AccessToken + `&actionKey=appkey&appkey=dfca71928277209b&area_id=207&build=1250004&buvid=` + buvid + `&c_locale=zh_CN&channel=huawei&click_id=&client_sign=` + clientSign + `&client_ts=` + clientTs + `&data_behavior_id=&data_source_id=&device=android&disable_rcmd=0&gu_id=` + guid + `&jump_from=27002&mobi_app=android_hd&parent_id=1&platform=android&play_type=&play_url=&player_type=0&room_id=` + roomID + `&s_locale=zh_CN&s_time=0&secret_key=` + secretKey + `&seq_id=` + strconv.Itoa(seqID) + `&session_id=-99998&statistics={"appId":5,"platform":3,"version":"1.25.0","abtest":""}&timestamp=` + timestamp + `&ts=` + StrTime() + `&up_id=` + upID + `&up_level=35&up_session=&uuid=` + tuuid + `&version=1.25.0&visit_id=` + visitid + `&watch_status=&watch_time=` + watchTime)
					r, err := req.C().R().SetContentType("application/x-www-form-urlencoded").SetBody(data).SetResult(&body).Post(uri)
					if err != nil {
						printf("mobileHeartBeat err: %v", err)
						break
					}
					if r.IsError() {
						print("mobileHeartBeat response isError")
						break
					}
					if body.Code != 0 {
						printf("mobileHeartBeat err: %v", body.Message)
						break
					}
					//printf("用户%v %v %v %v %v", userid, user.UserName, medal.NickName, medal.MedalName, seqID)
				}
			}
		}(medal)
	}
}

//0 数字+大写 1 数字+小写 2 数字+大小写 3 数字 4 5 16进制
func randStr(l, typ int, ran ...int) string {
	str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	if typ == 1 {
		str = "0123456789qwertyuiopasdfghjklzxcvbnm"
	} else if typ == 2 {
		str = "0123456789qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ"
	} else if typ == 3 {
		str = "0123456789"
	} else if typ == 4 {
		str = "0123456789abcdef"
	} else if typ == 5 {
		str = "0123456789ABCDEF"
	}
	bytes := []byte(str)
	result := []byte{}
	rr := time.Now().UnixNano()
	if len(ran) == 1 {
		rr = int64(ran[0])
	}
	r := rand2.New(rand2.NewSource(rr))

	for i := 0; i < l; i++ {
		ns := bytes[r.Intn(len(bytes))]
		if i == 0 && ns == bytes[0] {
			ns = bytes[1]
		}
		result = append(result, ns)
	}
	return string(result)
}
func randuuid() string {
	u1, err := uuid.NewV4()
	if err == nil {
		return u1.String()
	}
	return randuuid()
}
func randSleep(timea, timeb int, aa int) {
	if timeb <= 1 {
		return
	}
	rand.Seed(time.Now().UnixNano() - int64(aa))
	if timea < 0 {
		a := rand.Intn(timeb*1000 - 800)
		time.Sleep(time.Duration(int64(a) * 1000000))
	}
	b := timeb - timea
	c := rand.Intn(b*1000-1000) + timea*1000 + 500
	time.Sleep(time.Duration(int64(c) * 1000000))
}

func printMedals() {
	FansMedal()
	for k, user := range Btoml.BiliUser {
		fmt.Printf("用户%v %v:\n", k, user.UserName)
		uid := cookie2uid(user.Cookie)
		medals, ok := UserMedals[uid]
		if ok {
			for _, v := range medals {
				xunzhang := fmt.Sprintf("%v|%v", strLen(v.MedalName, 6), v.Level)
				if v.Level < 10 {
					xunzhang = xunzhang + " "
				}
				if v.IsLighted == 0 {
					xunzhang = xunzhang + "[X]  "
				} else {
					xunzhang = xunzhang + "     "
				}
				fmt.Printf("    %v\t[%v%%]\t今日:%v/%v \n", xunzhang, v.Intimacy*100/v.NextIntimacy, v.TodayFeed, v.DayLimit)

			}
		}
	}
}
func strLen(str string, l int) string {
	le := lenStr(str)
	if le < l {
		a := l - le
		for i := 0; i < a; i++ {
			str = str + " "
		}
	}
	return str
}
